from datetime import datetime
from typing import List, Optional, Union

from common.log_decorator import log_decorator
from common.params_check_decorator import params_check_decorator
from common.utils import format_validation_error

from mg_app_framework import HttpBasicHandler, MesCode
from pydantic import BaseModel, ValidationError, validator

from device_status_mgm.device_status_constant import DEVICE_STATUS_TRANCE_MAP
from device_status_mgm.service.device_status_process import device_heartbeat_report, device_equip_status_report, \
    device_equip_stop_reason_report, device_equip_alarm_report


class DeviceHeartbeatReportHandler(HttpBasicHandler):
    """
    设备在线心跳上报
    """

    class CheckModel(BaseModel):
        upperpc_code: str  # 上位机编码
        is_online: int  # 在线状态

    @validator('is_online', pre=True, always=True)
    def validate_is_online(cls, v):
        v = int(v)
        if v not in [0, 1]:
            raise ValueError("Incorrect is_online format, should be in 0~1")
        return v

    @log_decorator(log_exception=True)
    @params_check_decorator(check_params=["trace_id", "call_id", "upperpc_code"])
    async def post_process(self, *args, **kwargs):
        try:
            check_node = self.CheckModel(**self.data)
            upperpc_code = check_node.upperpc_code
            is_online = check_node.is_online
            await device_heartbeat_report(
                upperpc_code=upperpc_code,
                is_online=is_online,
            )
            self.send_response_data(MesCode.success, {}, "OK")
        except ValidationError as v_e:
            self.send_response_data(MesCode.fail, {}, format_validation_error(v_e.errors()))
        except Exception as e:
            self.send_response_data(MesCode.fail, {}, "设备在线心跳上报异常")


class DeviceEquipStatusReportHandler(HttpBasicHandler):
    """
    设备状态信息上报
    """

    class CheckModel(BaseModel):
        upperpc_code: str  # 上位机编码
        equip_code: str  # 设备编码
        equip_status: Union[str, int]  # 设备状态
        start_time: str  # 开始时间
        reason_code: Optional[str]  # 停机原因代码
        user_code: Optional[str]  # 用户编码

        @validator('start_time', pre=True, always=True)
        def validate_start_time(cls, v):
            try:
                datetime.strptime(v, '%Y-%m-%d %H:%M:%S')
            except ValueError:
                raise ValueError("Incorrect datetime format, should be %Y-%m-%d %H:%M:%S")
            return v

        @validator('equip_status', pre=True, always=True)
        def validate_equip_status(cls, v):
            if v not in DEVICE_STATUS_TRANCE_MAP:
                raise ValueError(f"Incorrect equip_status format, should be in {list(DEVICE_STATUS_TRANCE_MAP.keys())}")
            return DEVICE_STATUS_TRANCE_MAP[v]

    @log_decorator(log_exception=True)
    @params_check_decorator(check_params=["trace_id", "call_id", "upperpc_code", "equip_code"])
    async def post_process(self, *args, **kwargs):
        try:
            check_node = self.CheckModel(**self.data)
            upperpc_code = check_node.upperpc_code
            equip_code = check_node.equip_code
            equip_status = int(check_node.equip_status)
            start_time = check_node.start_time
            reason_code = check_node.reason_code
            user_code = check_node.user_code
            mark_suc, mark_info = await device_equip_status_report(
                upperpc_code=upperpc_code,
                equip_code=equip_code,
                equip_status=equip_status,
                start_time=start_time,
                reason_code=reason_code,
                user_code=user_code,
            )
            if not mark_suc:
                self.send_response_data(MesCode.fail, {}, mark_info)
            else:
                self.send_response_data(MesCode.success, {}, "OK")
        except ValidationError as v_e:
            self.send_response_data(MesCode.fail, {}, format_validation_error(v_e.errors()))
        except Exception as e:
            self.send_response_data(MesCode.fail, {}, "设备状态信息上报异常")


class DeviceEquipStopReasonReportHandler(HttpBasicHandler):
    """
    设备停机原因上报
    """

    class CheckModel(BaseModel):
        upperpc_code: str  # 上位机编码
        equip_code: str  # 设备编码
        start_time: str  # 开始时间
        reason_code: str  # 停机原因代码
        user_code: Optional[str]  # 用户编码

    @validator('start_time', pre=True, always=True)
    def validate_start_time(cls, v):
        try:
            datetime.strptime(v, '%Y-%m-%d %H:%M:%S')
        except ValueError:
            raise ValueError("Incorrect datetime format, should be %Y-%m-%d %H:%M:%S")
        return v

    @log_decorator(log_exception=True)
    @params_check_decorator(check_params=["trace_id", "call_id", "upperpc_code", "equip_code"])
    async def post_process(self, *args, **kwargs):
        try:
            check_node = self.CheckModel(**self.data)
            upperpc_code = check_node.upperpc_code
            equip_code = check_node.equip_code
            start_time = check_node.start_time
            reason_code = check_node.reason_code
            user_code = check_node.user_code
            mark_suc, mark_info = await device_equip_stop_reason_report(
                upperpc_code=upperpc_code,
                equip_code=equip_code,
                start_time=start_time,
                reason_code=reason_code,
                user_code=user_code,
            )
            if not mark_suc:
                self.send_response_data(MesCode.fail, {}, mark_info)
            else:
                self.send_response_data(MesCode.success, {}, "OK")
        except ValidationError as v_e:
            self.send_response_data(MesCode.fail, {}, format_validation_error(v_e.errors()))
        except Exception as e:
            self.send_response_data(MesCode.fail, {}, "设备停机原因上报异常")


class DeviceEquipAlarmReportHandler(HttpBasicHandler):
    """
    设备报警上报
    """

    class CheckModel(BaseModel):
        upperpc_code: str  # 上位机编码
        equip_code: str  # 设备编码
        user_code: Optional[str]  # 用户编码
        alarm_list: Optional[List] = []  # 报警列表

    @log_decorator(log_exception=True)
    @params_check_decorator(check_params=["trace_id", "call_id", "upperpc_code", "equip_code"])
    async def post_process(self, *args, **kwargs):
        try:
            check_node = self.CheckModel(**self.data)
            upperpc_code = check_node.upperpc_code
            equip_code = check_node.equip_code
            user_code = check_node.user_code
            alarm_list = check_node.alarm_list
            await device_equip_alarm_report(
                upperpc_code=upperpc_code,
                equip_code=equip_code,
                user_code=user_code,
                alarm_list=alarm_list,
            )
            self.send_response_data(MesCode.success, {}, "OK")

        except ValidationError as v_e:
            self.send_response_data(MesCode.fail, {}, format_validation_error(v_e.errors()))
        except Exception as e:
            self.send_response_data(MesCode.fail, {}, "设备报警上报异常")
